﻿/***************************************************************************
 * Copyright (c) reallyinfo Corporation 2019.
 * Developer: hainee
 * Email: hainee@163.com
 ***************************************************************************/
(function (root) {
  if (!HTMLCollection.prototype.forEach) {
    HTMLCollection.prototype.forEach = function (cb) {
      for (var i = 0; i < this.length; i++) {
        cb && cb(this[i], i)
      }
    }
  }

  if (!Element.prototype.insertAt) {
    Element.prototype.insertAt = function (ele, idx) {
      this.insertBefore(ele, this.children[idx])
    }
  }

  // ********************* content types ***********************
  const contentTypes = {
    calculationChain: 'application/vnd.openxmlformats-officedocument.spreadsheetml.calcChain+xml',
    cellMetadata: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheetMetadata+xml',
    chart: 'application/vnd.openxmlformats-officedocument.drawingml.chart+xml',
    chartColorStyle: 'application/vnd.ms-office.chartcolorstyle+xml',
    chartDrawing: 'application/vnd.openxmlformats-officedocument.drawingml.chartshapes+xml',
    chartsheet: 'application/vnd.openxmlformats-officedocument.spreadsheetml.chartsheet+xml',
    chartStyle: 'application/vnd.ms-office.chartstyle+xml',
    commentAuthors: 'application/vnd.openxmlformats-officedocument.presentationml.commentAuthors+xml',
    connections: 'application/vnd.openxmlformats-officedocument.spreadsheetml.connections+xml',
    coreFileProperties: 'application/vnd.openxmlformats-package.core-properties+xml',
    customFileProperties: 'application/vnd.openxmlformats-officedocument.custom-properties+xml',
    customization: 'application/vnd.ms-word.keyMapCustomizations+xml',
    customProperty: 'application/vnd.openxmlformats-officedocument.spreadsheetml.customProperty',
    customXmlProperties: 'application/vnd.openxmlformats-officedocument.customXmlProperties+xml',
    diagramColors: 'application/vnd.openxmlformats-officedocument.drawingml.diagramColors+xml',
    diagramData: 'application/vnd.openxmlformats-officedocument.drawingml.diagramData+xml',
    diagramLayoutDefinition: 'application/vnd.openxmlformats-officedocument.drawingml.diagramLayout+xml',
    diagramPersistLayout: 'application/vnd.ms-office.drawingml.diagramDrawing+xml',
    diagramStyle: 'application/vnd.openxmlformats-officedocument.drawingml.diagramStyle+xml',
    dialogsheet: 'application/vnd.openxmlformats-officedocument.spreadsheetml.dialogsheet+xml',
    digitalSignatureOrigin: 'application/vnd.openxmlformats-package.digital-signature-origin',
    documentSettings: 'application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml',
    drawings: 'application/vnd.openxmlformats-officedocument.drawing+xml',
    endnotes: 'application/vnd.openxmlformats-officedocument.wordprocessingml.endnotes+xml',
    excelAttachedToolbars: 'application/vnd.ms-excel.attachedToolbars',
    extendedFileProperties: 'application/vnd.openxmlformats-officedocument.extended-properties+xml',
    externalWorkbook: 'application/vnd.openxmlformats-officedocument.spreadsheetml.externalLink+xml',
    fontData: 'application/x-fontdata',
    fontTable: 'application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml',
    footer: 'application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml',
    footnotes: 'application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml',
    gif: 'image/gif',
    glossaryDocument: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document.glossary+xml',
    handoutMaster: 'application/vnd.openxmlformats-officedocument.presentationml.handoutMaster+xml',
    header: 'application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml',
    jpeg: 'image/jpeg',
    mainDocument: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml',
    notesMaster: 'application/vnd.openxmlformats-officedocument.presentationml.notesMaster+xml',
    notesSlide: 'application/vnd.openxmlformats-officedocument.presentationml.notesSlide+xml',
    numberingDefinitions: 'application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml',
    pict: 'image/pict',
    pivotTable: 'application/vnd.openxmlformats-officedocument.spreadsheetml.pivotTable+xml',
    pivotTableCacheDefinition: 'application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheDefinition+xml',
    pivotTableCacheRecords: 'application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheRecords+xml',
    png: 'image/png',
    presentation: 'application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml',
    presentationProperties: 'application/vnd.openxmlformats-officedocument.presentationml.presProps+xml',
    presentationTemplate: 'application/vnd.openxmlformats-officedocument.presentationml.template.main+xml',
    queryTable: 'application/vnd.openxmlformats-officedocument.spreadsheetml.queryTable+xml',
    relationships: 'application/vnd.openxmlformats-package.relationships+xml',
    ribbonAndBackstageCustomizations: 'http://schemas.microsoft.com/office/2009/07/customui',
    sharedStringTable: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml',
    singleCellTable: 'application/vnd.openxmlformats-officedocument.spreadsheetml.tableSingleCells+xml',
    slicerCache: 'application/vnd.openxmlformats-officedocument.spreadsheetml.slicerCache+xml',
    slicers: 'application/vnd.openxmlformats-officedocument.spreadsheetml.slicer+xml',
    slide: 'application/vnd.openxmlformats-officedocument.presentationml.slide+xml',
    slideComments: 'application/vnd.openxmlformats-officedocument.presentationml.comments+xml',
    slideLayout: 'application/vnd.openxmlformats-officedocument.presentationml.slideLayout+xml',
    slideMaster: 'application/vnd.openxmlformats-officedocument.presentationml.slideMaster+xml',
    slideShow: 'application/vnd.openxmlformats-officedocument.presentationml.slideshow.main+xml',
    slideSyncData: 'application/vnd.openxmlformats-officedocument.presentationml.slideUpdateInfo+xml',
    styles: 'application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml',
    tableDefinition: 'application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml',
    tableStyles: 'application/vnd.openxmlformats-officedocument.presentationml.tableStyles+xml',
    theme: 'application/vnd.openxmlformats-officedocument.theme+xml',
    themeOverride: 'application/vnd.openxmlformats-officedocument.themeOverride+xml',
    tiff: 'image/tiff',
    trueTypeFont: 'application/x-font-ttf',
    userDefinedTags: 'application/vnd.openxmlformats-officedocument.presentationml.tags+xml',
    viewProperties: 'application/vnd.openxmlformats-officedocument.presentationml.viewProps+xml',
    vmlDrawing: 'application/vnd.openxmlformats-officedocument.vmlDrawing',
    volatileDependencies: 'application/vnd.openxmlformats-officedocument.spreadsheetml.volatileDependencies+xml',
    webSettings: 'application/vnd.openxmlformats-officedocument.wordprocessingml.webSettings+xml',
    wordAttachedToolbars: 'application/vnd.ms-word.attachedToolbars',
    wordprocessingComments: 'application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml',
    wordprocessingTemplate: 'application/vnd.openxmlformats-officedocument.wordprocessingml.template.main+xml',
    workbook: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml',
    workbookRevisionHeader: 'application/vnd.openxmlformats-officedocument.spreadsheetml.revisionHeaders+xml',
    workbookRevisionLog: 'application/vnd.openxmlformats-officedocument.spreadsheetml.revisionLog+xml',
    workbookStyles: 'application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml',
    workbookTemplate: 'application/vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml',
    workbookUserData: 'application/vnd.openxmlformats-officedocument.spreadsheetml.userNames+xml',
    worksheet: 'application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml',
    worksheetComments: 'application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml',
    worksheetSortMap: 'application/vnd.ms-excel.wsSortMap+xml',
    xmlSignature: 'application/vnd.openxmlformats-package.digital-signature-xmlsignature+xml'
  }

  // *********** relationship types ***********
  const relationshipTypes = {
    alternativeFormatImport: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/aFChunk',
    calculationChain: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/calcChain',
    cellMetadata: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/sheetMetadata',
    chart: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart',
    chartColorStyle: 'http://schemas.microsoft.com/office/2011/relationships/chartColorStyle',
    chartDrawing: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/chartUserShapes',
    chartsheet: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/chartsheet',
    chartStyle: 'http://schemas.microsoft.com/office/2011/relationships/chartStyle',
    commentAuthors: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/commentAuthors',
    connections: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/connections',
    coreFileProperties: 'http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties',
    customFileProperties: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties',
    customization: 'http://schemas.microsoft.com/office/2006/relationships/keyMapCustomizations',
    customProperty: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/customProperty',
    customXml: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/customXml',
    customXmlMappings: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/xmlMaps',
    customXmlProperties: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/customXmlProps',
    diagramColors: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/diagramColors',
    diagramData: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/diagramData',
    diagramLayoutDefinition: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/diagramLayout',
    diagramPersistLayout: 'http://schemas.microsoft.com/office/2007/relationships/diagramDrawing',
    diagramStyle: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/diagramQuickStyle',
    dialogsheet: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/dialogsheet',
    digitalSignatureOrigin: 'http://schemas.openxmlformats.org/package/2006/relationships/digital-signature/origin',
    documentSettings: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings',
    drawings: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing',
    endnotes: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/endnotes',
    excelAttachedToolbars: 'http://schemas.microsoft.com/office/2006/relationships/attachedToolbars',
    extendedFileProperties: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties',
    externalWorkbook: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/externalLink',
    font: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/font',
    fontTable: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/fontTable',
    footer: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer',
    footnotes: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/footnotes',
    glossaryDocument: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/glossaryDocument',
    handoutMaster: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/handoutMaster',
    header: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/header',
    image: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image',
    mainDocument: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument',
    notesSlide: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesSlide',
    numberingDefinitions: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/numbering',
    pivotTable: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotTable',
    pivotTableCacheDefinition: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotCacheDefinition',
    pivotTableCacheRecords: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotCacheRecords',
    presentation: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument',
    presentationProperties: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/presProps',
    queryTable: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/queryTable',
    ribbonAndBackstageCustomizations: 'http://schemas.microsoft.com/office/2007/relationships/ui/extensibility',
    sharedStringTable: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings',
    singleCellTable: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/tableSingleCells',
    slide: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide',
    slideComments: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments',
    slideLayout: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout',
    slideMaster: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideMaster',
    slideSyncData: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideUpdateInfo',
    styles: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles',
    tableDefinition: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/table',
    tableStyles: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/tableStyles',
    theme: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme',
    themeOverride: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/themeOverride',
    thumbnail: 'http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail',
    userDefinedTags: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/tags',
    viewProperties: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/viewProps',
    vmlDrawing: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing',
    volatileDependencies: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/volatileDependencies',
    webSettings: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/webSettings',
    wordAttachedToolbars: 'http://schemas.microsoft.com/office/2006/relationships/attachedToolbars',
    wordprocessingComments: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments',
    workbook: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument',
    workbookRevisionHeader: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/revisionHeaders',
    workbookRevisionLog: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/revisionLog',
    workbookStyles: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles',
    workbookUserData: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/usernames',
    worksheet: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet',
    worksheetComments: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments',
    worksheetSortMap: 'http://schemas.microsoft.com/office/2006/relationships/wsSortMap',
    xmlSignature: 'http://schemas.openxmlformats.org/package/2006/relationships/digital-signature/signature'
  }

  const XML_FILE_PREFIX = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'

  const PACKAGE_RELATIONSHAIPS_XML = '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">'

  const NAMESPACE = {
    p: 'http://schemas.openxmlformats.org/presentationml/2006/main'
  }

  function getRelativePath (rootPath, relativePath) {
    var paths = rootPath.split('/')
    if (rootPath.endsWith('/')) {
      paths.pop()
    }
    var relativePaths = relativePath.split('/')
    while (relativePaths[0] === '..') {
      paths.pop()
      relativePaths.shift()
    }
    return `${paths.join('/')}/${relativePaths.join('/')}`
  }

  function throwErrorInvalidDocumentFileNotFound (file) {
    throw new Error(`文档格式无效，未找到指定文件[${file}]！`)
  }

  /**
   * 定义openxml模板渲染对象，可使用art-template语法渲染xml所有文件
   * js库依赖：JSZip(https://github.com/Stuk/jszip)、art-template(https://github.com/aui/art-template)
   * @param {any} JSZip
   * @param {any} template
   * @returns JSZip
   */
  function defineOpenXmlXP (JSZip, template) {
    const CONTENT_TYPE_FILE = '[Content_Types].xml'

    async function renderFile (docJSZip, fileName, dataObject) {
      const ext = fileName.substring(fileName.lastIndexOf('.')).toLowerCase()
      if (ext === '.xml' || ext === '.rels') {
        // 如果是xml文档，则直接进行渲染
        try {
          var xmlFile = docJSZip.files[fileName]
          var content = await xmlFile.async('text')
          if (content.indexOf('{{') >= 0) {
            var xml = template.render(content, dataObject)
            if (xml !== content) {
              docJSZip.file(fileName, xml)
            }
          }
        } catch (ex) {
          console.error(`render file error:${fileName}`)
          debugger
        }
      } else if (ext === '.xlsx') {
        // 如果是嵌入的数据资源，则递归渲染
        var xlsxFileData = await docJSZip.files[fileName].async('uint8array')
        var xlsxJSZip = new JSZip()
        await xlsxJSZip.loadAsync(xlsxFileData)
        await renders.renderZip(xlsxJSZip, dataObject)
        try {
          xlsxFileData = await xlsxJSZip.generateAsync({ type: 'uint8array' })
          docJSZip.file(fileName, xlsxFileData, { binary: true })
        } catch (ex) {
          debugger
        }
      }
    }

    function getFileInfo (filePath) {
      const paths = filePath.split('/')
      const name = paths.pop()
      const idx = name.indexOf('.')
      const orgName = idx >= 0 ? name.substring(0, idx) : name
      const ext = idx >= 0 ? name.substring(idx) : ''
      return {
        fullPath: filePath,
        path: paths.join('/'),
        name,
        orgName,
        ext
      }
    }

    const renders = {
      getFileInfo: getFileInfo,
      async renderZip (docJSZip, dataObject, onProgressCallback) {
        var contentTypeFile = docJSZip.files[CONTENT_TYPE_FILE]
        if (contentTypeFile === null) {
          throwErrorInvalidDocumentFileNotFound(CONTENT_TYPE_FILE)
        }
        var files = Object.keys(docJSZip.files)
        for (var i = 0; i < files.length; i++) {
          await renderFile(docJSZip, files[i], dataObject)
          onProgressCallback &&
            onProgressCallback(parseInt(((i + 1) * 100) / files.length))
        }
        return docJSZip
      },

      async renderBlob (fileData, dataObject, onProgressCallback) {
        var zip = await this.loadBlob(fileData)
        return this.renderZip(zip, dataObject, onProgressCallback)
      },

      async renderBase64 (fileData, dataObject, onProgressCallback) {
        var zip = await this.loadBase64(fileData)
        return this.renderZip(zip, dataObject, onProgressCallback)
      },

      async loadBase64 (fileData) {
        var zip = new JSZip()
        await zip.loadAsync(fileData, { base64: true })
        return zip
      },

      async loadBlob (fileData) {
        var zip = new JSZip()
        await zip.loadAsync(fileData, { binary: true })
        return zip
      },

      async getFiles (zipFile, createItemCB) {
        let root = {
          name: '/',
          path: '/',
          zipFile: zipFile,
          items: [],
          dir: true
        }
        var itemCounter = 0
        createItemCB && (await createItemCB(root, itemCounter))
        let allFolders = {
          '/': root
        }
        let regxNum = /(\d+)\.{1}/
        let regxPrefix = /^[^\d]+/
        let keys = Object.keys(zipFile.files).sort((a, b) => {
          let prefixA = a.match(regxPrefix)
          let prefixB = b.match(regxPrefix)
          if (prefixA.length && prefixA[0] === prefixB[0]) {
            let numA = parseInt(a.match(regxNum))
            let numB = parseInt(b.match(regxNum))
            return numA - numB
          }
          return a.localeCompare(b)
        })
        for (var i = 0; i < keys.length; i++) {
          let key = keys[i]
          itemCounter++
          let item = zipFile.files[key]
          let paths = key.split('/')
          paths = paths.filter(k => k.length > 0)
          let name = paths.pop()
          let path = `${paths.join('/')}/`
          let folder = root
          if (path !== '/') {
            if (!allFolders[path]) {
              let tmpPaths = []
              let tmpFolders = []
              path
                .split('/')
                .filter(p => p.length > 0)
                .forEach(p => {
                  tmpPaths.push(p)
                  let tmpPath = `${tmpPaths.join('/')}/`
                  let tmpFolder = allFolders[tmpPath]
                  if (!tmpFolder) {
                    allFolders[tmpPath] = tmpFolder = {
                      name: p,
                      path: tmpPath,
                      items: [],
                      root,
                      dir: true
                    }
                    folder.items.push(tmpFolder)
                    tmpFolders.push(tmpFolder)
                  }
                  folder = tmpFolder
                })
              if (createItemCB) {
                for (var t = 0; t < tmpFolders.length; t++) {
                  itemCounter++
                  await createItemCB(tmpFolders[t], itemCounter)
                }
              }
            } else {
              folder = allFolders[path]
            }
          }
          if (!item.dir) {
            let file = getFileInfo(key)
            file.dir = false
            file.root = root
            file.folder = folder
            createItemCB && (await createItemCB(file, itemCounter))
            folder.items.push(file)
          }
        }
        return [root]
      },

      /**
       * 将指定xml的zip文件解析成xml文档对象
       * @param {any} zipFile
       * @param {any} type
       */
      async getXmlDocument (zipFile, type) {
        try {
          var content = await zipFile.async(type || 'text')
          var parser = new DOMParser()
          return parser.parseFromString(content, 'text/xml')
        } catch (ex) {
          debugger
        }
      },

      /**
       * 加载指定路径文档的所有关联文件
       * @param {any} zip 文档ZIP对象
       * @param {any} files 文件的路径，可以是","号分隔的字符串 或 Array
       */
      async loadDocFiles (zip, files) {
        var fileCaches = {}
        var fileObjs = []
        async function _loadFile (docJSZip, filePath, fileCaches) {
          console.log(`load file ${filePath}`)
          if (fileCaches[filePath]) {
            return fileCaches[filePath]
          }
          var rels = []
          var fileInfo = getFileInfo(filePath)
          var docFile = docJSZip.file(filePath)
          if (!docFile) {
            throw new Error(`未找到文档文件：'${filePath}'！`)
          }
          var relsFile = docJSZip.file(
            `${fileInfo.path}/_rels/${fileInfo.name}.rels`
          )
          var fileResult = (fileCaches[filePath] = {
            file: docFile,
            fileInfo,
            rels,
            type: relationshipTypes.officeDocument
          })
          if (relsFile != null) {
            const relsDoc = await this.getXmlDocument(relsFile)
            // 获取文档的rels
            var relationships = relsDoc.querySelectorAll('Relationship') || []
            for (var i = 0; i < relationships.length; i++) {
              var item = relationships[i]
              var targetFile = item.getAttribute('Target')
              var id = item.getAttribute('Id')
              var type = item.getAttribute('Type')
              var targetFilePath = getRelativePath(fileInfo.path, targetFile)
              var target = null
              switch (type) {
                case relationshipTypes.slideLayout:
                case relationshipTypes.slideMaster:
                case relationshipTypes.vmlDrawing:
                case relationshipTypes.chart:
                  target = await _loadFile.call(
                    this,
                    docJSZip,
                    targetFilePath,
                    fileCaches
                  )
                  break

                default:
                  if (fileCaches[targetFilePath]) {
                    target = fileCaches[targetFilePath]
                  } else {
                    fileCaches[targetFilePath] = target = {
                      type,
                      file: docJSZip.file(targetFilePath),
                      fileInfo: getFileInfo(targetFilePath)
                    }
                  }
                  break
              }
              rels.push({
                id,
                type,
                target,
                targetFile
              })
            }
          }
          return fileResult
        }

        files = files instanceof Array ? files : files.split(',')
        for (var i = 0; i < files.length; i++) {
          const filePath = files[i]
          const fileObj = await _loadFile.call(this, zip, filePath, fileCaches)
          fileObjs.push(fileObj)
        }
        return fileObjs
      },

      /**
       * 将若干个slide复制并添加到当前幻灯片的指定索引位置
       * @param {any} targetDocJSZip  当前文档ZIP文件对象
       * @param {any} srcDocJSZip  源文档ZIP文件对象
       * @param {Object} srcSlideNames  源Slide名称：slide1,slide2 或 ['slide1', 'slide2']
       * @param {Function} progressCB 进度回调
       * @param {any} insertPos  要被插入的位置，默认最后面
       */
      async copySlideFrom (
        targetDocJSZip,
        srcDocJSZip,
        srcSlideNames,
        progressCB,
        insertPos
      ) {
        const presentationPath = 'ppt/presentation.xml'
        const presentationRelsPath = 'ppt/_rels/presentation.xml.rels'

        var contentTypesFile = targetDocJSZip.file(CONTENT_TYPE_FILE)
        if (!contentTypesFile) {
          throw new Error(`未找到文件：'${CONTENT_TYPE_FILE}'`)
        }
        var contentTypesDoc = await this.getXmlDocument(contentTypesFile)
        var contentTypesListEle = contentTypesDoc.documentElement

        insertPos = parseInt(insertPos)
        if (isNaN(insertPos) || insertPos < 0) {
          insertPos = -1
        }
        var targetPresentationDoc = await this.getXmlDocument(
          targetDocJSZip.file(presentationPath)
        )
        var targetSlideIdList = targetPresentationDoc.getElementsByTagNameNS(
          NAMESPACE.p,
          'sldIdLst'
        )[0]
        if (!targetSlideIdList) {
          throw new Error('未找到节点[p:sldIdLst]！')
        }
        var targetPresentationDocRels = await this.getXmlDocument(
          targetDocJSZip.file(presentationRelsPath)
        )
        var targetSlideIdListRels = targetPresentationDocRels.querySelector(
          'Relationships'
        )
        var maxSlideId = 1

        targetSlideIdList.children.forEach(item => {
          maxSlideId = Math.max(maxSlideId, parseInt(item.getAttribute('id')))
        })
        maxSlideId++
        if (srcSlideNames && srcSlideNames.length) {
          srcSlideNames =
            srcSlideNames instanceof Array
              ? srcSlideNames
              : srcSlideNames.split(',')
        } else {
          // 复制全部slide
          srcSlideNames = []
          var srcPresentationDoc = await this.getXmlDocument(
            srcDocJSZip.file(presentationPath)
          )
          var srcPresentationDocRels = await this.getXmlDocument(
            srcDocJSZip.file(presentationRelsPath)
          )
          var srcSlideIdList = srcPresentationDoc.getElementsByTagNameNS(
            NAMESPACE.p,
            'sldId'
          )
          srcSlideIdList.forEach(item => {
            var slideId = item.getAttribute('r:id')
            var slideRel = srcPresentationDocRels.querySelector(
              `Relationship[Id=${slideId}]`
            )
            var slideFile = slideRel.getAttribute('Target')
            var slideFileInfo = getFileInfo(slideFile)
            srcSlideNames.push(slideFileInfo.orgName)
          })
        }
        var srcSlideFiles = srcSlideNames.map(item => `ppt/slides/${item}.xml`)
        var srcSlideObjs = await this.loadDocFiles(srcDocJSZip, srcSlideFiles)
        var fileCaches = {}

        async function copyDocFile (srcDocFile) {
          var srcDocName = srcDocFile.file.name
          if (fileCaches[srcDocName]) {
            return
          }
          fileCaches[srcDocName] = true
          let fileInfo = srcDocFile.fileInfo
          let newFileName = `${fileInfo.orgName}_${maxSlideId}${fileInfo.ext}`
          let newFilePath = `${fileInfo.path}/${newFileName}`
          let type = srcDocFile.type || relationshipTypes.officeDocument
          var fileData = await srcDocFile.file.async('uint8array')
          switch (type) {
            case relationshipTypes.officeDocument:
            case relationshipTypes.chart:
            case relationshipTypes.slideLayout:
            case relationshipTypes.slideMaster:
            case relationshipTypes.userDefinedTags:
            case relationshipTypes.drawings:
              if (srcDocFile.rels) {
                var relationshipsXml = [
                  XML_FILE_PREFIX,
                  PACKAGE_RELATIONSHAIPS_XML
                ]
                for (var i = 0; i < srcDocFile.rels.length; i++) {
                  let relFile = srcDocFile.rels[i]
                  await copyDocFile(relFile.target)
                  let relTargetFileInfo = getFileInfo(relFile.targetFile)
                  let newRelFileTarget = `${relTargetFileInfo.path}/${relTargetFileInfo.orgName}_${maxSlideId}${relTargetFileInfo.ext}`
                  relationshipsXml.push(
                    `  <Relationship Id="${relFile.id}" Type="${relFile.type}" Target="${newRelFileTarget}"/>`
                  )
                }
                relationshipsXml.push('</Relationships>')
                let relationshipsContent = relationshipsXml.join('\r\n')
                let newRelFilePath = `${fileInfo.path}/_rels/${newFileName}.rels`
                targetDocJSZip.file(newRelFilePath, relationshipsContent, {
                  binary: false
                })
              }
          }
          targetDocJSZip.file(newFilePath, fileData, { binary: true })
          return newFilePath
        }

        for (var i = 0; i < srcSlideObjs.length; i++) {
          let srcSlide = srcSlideObjs[i]

          // 复制slide?.xml
          await copyDocFile(srcSlide)

          // 处理presentation.xml.rels
          let relationshipEle = targetPresentationDocRels.createElement(
            'Relationship'
          )
          let slideId = `rId${maxSlideId}`
          relationshipEle.setAttribute('Id', slideId)
          relationshipEle.setAttribute('Type', relationshipTypes.slide)
          relationshipEle.setAttribute(
            'Target',
            `slides/${srcSlide.fileInfo.orgName}_${maxSlideId}.xml`
          )
          targetSlideIdListRels.appendChild(relationshipEle)

          // 处理presentation.xml
          let sldIdEle = targetPresentationDoc.createElement('p:sldId')
          sldIdEle.setAttribute('id', maxSlideId)
          sldIdEle.setAttribute('r:id', slideId)
          if (
            insertPos === -1 ||
            insertPos >= targetSlideIdList.children.length
          ) {
            targetSlideIdList.appendChild(sldIdEle)
          } else {
            targetSlideIdList.insertAt(sldIdEle, insertPos)
            insertPos++
          }

          progressCB &&
            progressCB({
              percent: ((i + 1) * 100) / srcSlideObjs.length,
              slide: srcSlide
            })
          maxSlideId++
        }

        // 更新presentation.xml、presentation.xml.rels
        targetDocJSZip.file(
          presentationPath,
          targetPresentationDoc.documentElement.outerHTML,
          { binary: false }
        )
        targetDocJSZip.file(
          presentationRelsPath,
          targetPresentationDocRels.documentElement.outerHTML,
          { binary: false }
        )

        // 更新[Content_Types].xml
        // to do...

        return targetDocJSZip
      }
    }
    return renders
  }

  // module export
  if (typeof define === typeof function () { } && define.amd) { // AMD
    define('openxmlXP', ['JSZip', 'template'], function (jsZip, template) {
      return defineOpenXmlXP(jsZip, template)
    })
  } else if (typeof module !== typeof undefined && module.exports) { // Node
    module.exports = defineOpenXmlXP(JSZip, template)
  } else {
    root.openxmlXP = defineOpenXmlXP(JSZip, template)
  }
})(this)
